﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using ScrewTurn.Wiki;
using System.Data.SqlClient;
using System.Configuration;
using LuaInterface;
using ScrewTurn.Wiki.PluginFramework;
using System.Web.UI;


namespace UniBasel.Biozentrum.NMR
{
	public static class GenerateMacro
	{
		private static string LuaParser(string ExpContent, string SampleContent, string SpecContent)
		{
			string result;
			result = "Experiment Content:\n" + ExpContent + "\n" + "Sample Content:\n" + SampleContent + "\n" + "Spectrometer Content:\n" + SpecContent + "\n";

			Lua MyLua = new Lua();

            #region TestParameters
//            #region Spectrometer
//            //Avance 800 @ Biozentrum Basel
//            SpecContent = @"now=""selected"";
//spectrometer ={
//[""selected""]={
//
//B_field = 800.18, -- MHz
// proton_carrier_298k= 3740.31, 
// calibration_power_db_f1= 2., --db
// calibration_pulse90_f1= 9.833, 
// calibration_power_db_f2=-0.75, --db
// calibration_pulse90_f2=17.6, --mks
// calibration_power_db_f3=-3.2+0.87, --db
// calibration_pulse90_f3=38+4, --mks
// calibration_power_db_f4=0.3, --db
// calibration_pulse90_f4=160, --mks,
// home_topspin=""/u/exp/stan/nmr/lists""
//}
//}
//pulses ={
//watergate90=(1000.0*700.13/(spectrometer[now].B_field)), --mks
//COdec90=300.0*700.13/spectrometer[now].B_field, --mks
//Ncpd90=120.0*700.13/spectrometer[now].B_field, --mks
//Ccpd90=85.0*700.13/spectrometer[now].B_field, --mks
//Hcpd90=80.0*700.13/spectrometer[now].B_field, --mks
//Dcpd90=300.0*700.13/spectrometer[now].B_field, --mks
//Htocsy90=25.0*700.13/spectrometer[now].B_field, --mks
//--Bruker pulses
//
//bru_CaCb_180 = 210*750.13/(spectrometer[now].B_field), --mks inv pulse with Q3 shape
//bru_CaCb_90 = 308*750.13/(spectrometer[now].B_field), --mks excitation pulse with Q5 shape
//bru_Crp60=(500.0*600.13/(spectrometer[now].B_field)) --mks
//
//}
//
//IndirectChemicalShiftReferences={ -- from Cavanagh etc, page 263
//-- compound     ratio     Secondary reference : DSS , 2,2-Dimethyl-2-silapentane-5-sulfonic acid
//H2=0.153506088, -- DSS (internal)
//C13=0.251449530, -- DSS (internal)
//N15=0.101329118, -- Liquid NH3 (external)
//P31=0.404808636 -- (CH3O)3PO (internal)
//}
//";
//            #endregion

//            #region Sample
//            //Ubiquitin
//            SampleContent = @"
//spectrum ={
//N =  {carrier=119., sw=42.}, --ppm center, sw  
//Ca=  {carrier=56.,  sw=40.},   
//CaCb={carrier=48.5,  sw=70.}, 
//CO=  {carrier=173., sw=16.},
//Carom={carrier=110.,sw=20.},
//D=   {carrier=3.8, sw=14.},
//H=   {carrier=4.73, sw=12.},
//del_3_9_19= 0.000150 -- for 3-9-19, d5=1/d, d to next null (Hz)
//}
//
//-- hetero-nuclear J couplings
//couplings ={
//J_HN =   {Hz=91., range={min=89., max=92.}}, -- Hz
//J_HC = {Hz=145., range={min=130., max=160.}}, -- Hz
//J_HaCa = {Hz=140., range={min=140., max=140.}}, -- Hz
//J_HbCb = {Hz=130., range={min=140., max=140.}}, -- Hz
//J_HmCm = {Hz=125., range={min=140., max=140.}}, -- Hz  methyl
//J_CC =   {Hz=35., range={min=140., max=140.}}, -- Hz  methyl
//J_CaCb = {Hz=35., range={min=140., max=140.}}, -- Hz
//J_CaCO = {Hz=55., range={min=140., max=140.}}, -- Hz
//J_CON =  {Hz=15., range={min=140., max=140.}}, -- Hz
//J_CaN =  {Hz=11., range={min=10., max=15.}}, -- Hz
//J_CaN_1 =  {Hz=7., range={min=140., max=140.}}, -- Hz
//J_CON_1 =  {Hz=1., range={min=140., max=140.}}, -- Hz
//-- homo-nuclear J couplings
//J_HnHa = {Hz=6., range={min=3., max=12.}}, -- Hz
//J_HaHb = {Hz=6., range={min=3., max=12.}} -- Hz
//}
//";
//            #endregion

//            #region Experiment
//            //trosy_HNCA
//            ExpContent = @"exp_environment = {
//
//[""pulprog""]= ""tr_hnca_gp_2h.bru"",
//o1 = spectrometer[now].proton_carrier_298k,
//o2p=spectrum.Ca.carrier,
//o3p=spectrum.N.carrier,
//o4p=spectrum.D.carrier,
//[""xcpr""]=""parmode 3D"",
//--scripts
//[""script1""]=""hnca.m"", --Mathematica script
//[""script2""]=""tr_hnca_hr.m"", -- Mathematica script
//
//--set hard powers and pulses for f1-f4
//pl0 = 120, --dB
//
//p1 = spectrometer[now].calibration_pulse90_f1,
//pl1= spectrometer[now].calibration_power_db_f1,
//pl3= spectrometer[now].calibration_power_db_f3,
//
//-- D decoupling
//pcpd4 = pulses.Dcpd90, 
//pl17 = calc_power_set(pulses.Dcpd90,1.0,spectrometer[now].calibration_pulse90_f4,spectrometer[now].calibration_power_db_f4),
//[""cpdprg4""]= ""waltz16"", 
//
//--set soft powers and pulses
//p11 = pulses.watergate90, -- assume that watergate90 mks is ideal for 700
//[""spnam1""]= ""Sinc1.1000"", -- integral factor 0.5889
// sp1= calc_power_set(pulses.watergate90,0.5889,spectrometer[now].calibration_pulse90_f1,spectrometer[now].calibration_power_db_f1),
// spoal1 =0.5, -- have to check what's that
// spoffs1=0,
//
//--CaCb soft pulses
//p13 =pulses.bru_CaCb_90, 
//[""spnam2""] =""Q5.1000"", --intergal 0.0545
//sp2= calc_power_set(pulses.bru_CaCb_90,0.0545,spectrometer[now].calibration_pulse90_f2,spectrometer[now].calibration_power_db_f2), 
//spoal2 = 0.500,
//spoffs2 = 0.0,
//
//sp8= calc_power_set(pulses.bru_CaCb_90,0.0545,spectrometer[now].calibration_pulse90_f2,spectrometer[now].calibration_power_db_f2),
//[""spnam8""] =""Q5tr.1000"", 
//spoal8 = 0.500,
//spoffs8 = 0.0,
//
//p14 = pulses.bru_CaCb_180,
//[""spnam3""] =""Q3.1000"", 
//sp3= calc_power_set(0.5*pulses.bru_CaCb_180,0.1515,spectrometer[now].calibration_pulse90_f2,spectrometer[now].calibration_power_db_f2),
//spoal3 = 0.500,
//spoal3 = 0.00,
//
//cnst21 = spectrum.CO.carrier,
//cnst23 = spectrum.CaCb.carrier,
//
//[""spnam5""] =""Q3.1000"", --CO inversion pulse, p14
//sp5= calc_power_set(0.5*pulses.bru_CaCb_180,0.1515,spectrometer[now].calibration_pulse90_f2,spectrometer[now].calibration_power_db_f2),
//spoal5 = 0.500,
// 
//
//p21 = spectrometer[now].calibration_pulse90_f3,
//
//p16 = 1000, --1ms
//d1 = 1,
//d16 = 0.00015, --150 mks, for gradient recovery
//d11=30e-3, --delay for disk I/O 
//d12=20e-6, --delay for power switching
//
//--My gradients
//gpnam4 =""SINE.100"",
//gpz4 =90,
//p24 =900, --(Z-Filter)
//
//
// --set increment times
//d0=3e-6,
//d10=3e-6,
//in0 = (1/4/(spectrum.Ca.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.C13)),
//in10 = (1/2/(spectrum.N.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.N15)),
//in30 = (1/2/(spectrum.N.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.N15)),
//
//[""1 in_010 ""] = (1/4/(spectrum.Ca.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.C13)),
//[""1 nd_010""]= 4 , --C direction 
//
//[""2 in_010 ""] = (1/2/(spectrum.N.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.N15)),
//[""2 nd_010""]= 2 , --C direction 
//
//
//  --set transfer delays
// d23= 0.8*1/(4.*couplings.J_CaN.Hz),
// d26= 1/(4.*couplings.J_HN.Hz),
// d28= 0.8*1/(4.*couplings.J_CaCb.Hz),
// 
// [""1 td ""]=(0.8*1/(4.*couplings.J_CaCb.Hz))/((1/4/(spectrum.Ca.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.C13))),
// [""2 td""]= 2*(10.9e-3)/((1/2/(spectrum.N.sw*spectrometer[now].B_field*IndirectChemicalShiftReferences.N15))),
// 
// --set gradients
//[""gpnam1""]=""SINE.100"",
//[""gpnam2""]=""SINE.100"",
//[""gpnam3""]=""SINE.100"",
//gpz1 =60,
//gpz2 =32,
//gpz3 =88,
//
//--acqu modes
//[""1 FnMODE""]= ""States-TPPI"",
//[""2 FnMODE""]= ""Echo-Antiecho"",
//--processing
//[""1 mc2""]= ""States-TPPI"",
//[""2 mc2""]= ""echo-antiecho"",
//[""aqorder""]= ""3-2-1"",
//
//[""3 si""]= 2048, 
//[""3 stsi""]= 1024,
//[""1 stsi""]= 0,
//[""2 stsi""]= 0,
//[""2 si""]= 128, -- N dim
//[""1 si""]= 256, -- C dim 
//[""2 phc0""]= 90, -- n dim
//[""2 phc1""]= 0, -- n dim 
//[""1 phc0""]=  0, -- c dim
//[""1 phc1""]=  0, -- c dim 
//[""3 ssb""]=  2, -- n dim
//[""2 ssb""]=  2, -- n dim
//[""1 ssb""]=  2, -- n dim 
//[""1 WDW""]=""QSINE"",
//[""2 WDW""]=""QSINE"",
//[""3 WDW""]=""QSINE"",
//[""1 PH_mod""]=  ""pk"", -- n dim
//[""2 PH_mod""]=  ""pk"", -- n dim 
//[""3 PH_mod""]=  ""pk"", -- n dim 
//[""2 sf""]=spectrometer[now].B_field*IndirectChemicalShiftReferences.N15, -- N dim 
//[""1 sf""]=spectrometer[now].B_field*IndirectChemicalShiftReferences.C13, -- c dim 
//[""1 absf1""]= 100, 
//[""1 absf2""]= -100, 
//[""2 absf1""]= 100, 
//[""2 absf2""]= -100, 
//[""3 absf1""]= 12, 
//[""3 absf2""]= 6 ,
//[""2 REVERSE""]=""FALSE"", --N dim
//[""1 REVERSE""]=""FALSE"" --C dim
//}
//";
//            #endregion
            #endregion

			#region CalcPowerSet
			string CalcPowerSet = @"function calc_power_set(curren_pulse90,reference_shape_constant,calibration_pulse90,calibration_power_db) 	 
 local reference_rect_pulse90 = 10.; --mks --from NMRSIM
 local reference_rect_power_rf=50000.; --Hz       NMRSIM
 local calibration_constant_db = 0.;
 local out_put=120;

out_put=20.*math.log10(reference_shape_constant*curren_pulse90/(calibration_pulse90))+calibration_power_db;
return out_put;
end";
			#endregion

			MyLua.DoString(CalcPowerSet);
			
			MyLua.DoString(SampleContent);

			MyLua.DoString(SpecContent);

			MyLua.DoString(ExpContent);

			string ExportMacroPath =Settings.PublicDirectory+"LuaScripts\\ExportMacro.lua"; 
			MyLua.DoFile(ExportMacroPath);
			MyLua.DoString("ExportMacro(exp_environment);");
			result = (string)MyLua["Result"];
			Debug.WriteLine((string)MyLua["TestResult"]);
			MyLua.Dispose();
			return result;
		}

		public static string Generate(string ExpID, string SampleID, string SpectrometerID, string Remark, string UserName)
		{
			string NewSetupID = null;

			string ExpContent = null;
			string ExpName = null;

			string SampleContent = null;
			string SampleName = null;
			string SampleVersion = null;

			string SpectrometerContent = null;
			string SpectrometerName = null;

			#region Get The Contents of Exp, Sample and Spec from Database
			SqlConnection MyConnection = new SqlConnection(Constants.NMRUserDataConStr);
			MyConnection.Open();

			SqlCommand getSSContentByID = MyConnection.CreateCommand();
			getSSContentByID.CommandText = String.Format(@"SELECT samples.[content] AS SamCon, samples.[name] AS SamName, 
samples.[version] AS SamVer, spectrometers.[content] AS SpecCon, spectrometers.[name] AS SpecName
FROM            samples CROSS JOIN
                         spectrometers
WHERE        (samples.id = {0}) AND (spectrometers.id = {1})", SampleID, SpectrometerID);

			SqlDataReader MyReader = getSSContentByID.ExecuteReader();

			if (!MyReader.HasRows)
			{
				throw new NotImplementedException("Generate Method did not find Specified Sample and Spectrometer");
			}
			MyReader.Read();
			SampleContent = MyReader["SamCon"].ToString();
			SpectrometerContent = MyReader["SpecCon"].ToString();
			SampleName = MyReader["SamName"].ToString();
			SampleVersion = MyReader["SamVer"].ToString();
			SpectrometerName = MyReader["SpecName"].ToString();
			MyReader.Close();
			MyConnection.Close();

			MyConnection = new SqlConnection(Constants.NMRExperimentsConStr);
			MyConnection.Open();

			SqlCommand getExpContentByID = MyConnection.CreateCommand();
			getExpContentByID.CommandText = String.Format(@"SELECT experiments.[content] AS ExpCon, experiments.[name] AS ExpName
			FROM experiments WHERE (experiments.id={0})", ExpID);

			MyReader = getExpContentByID.ExecuteReader();
			if (!MyReader.HasRows)
			{
				throw new NotImplementedException("Generate method did not find specified Experiment.");
			}

			MyReader.Read();
			ExpContent = MyReader["ExpCon"].ToString();
			ExpName = MyReader["ExpName"].ToString();
			MyReader.Close();

			MyConnection.Close();
			#endregion

			string MacroContent = null;
			MacroContent = LuaParser(ExpContent, SampleContent, SpectrometerContent);

			#region Add Setup to Database

			string NewName = SampleName + "-Ver_" + SampleVersion + "-" + ExpName + "-" + SpectrometerName;
			if (NewName.Length > 50) NewName = NewName.Substring(0, 50);


			MyConnection = new SqlConnection(Constants.NMRUserDataConStr);
			MyConnection.Open();

			SqlCommand InsertNewSetup = MyConnection.CreateCommand();
			InsertNewSetup.CommandText = String.Format(@"
INSERT INTO setups ([content], remark, [name], experimentid, experimentname, sampleid, samplename, spectrometerid, spectrometername)
VALUES        ('{0}','{1}', '{2}', {3}, '{4}', {5}, '{6}', {7}, '{8}')", MacroContent.Replace("'", "''"), Remark.Replace("'", "''"), NewName,
														 ExpID, ExpName, SampleID, SampleName, SpectrometerID, SpectrometerName);
			MyReader = InsertNewSetup.ExecuteReader();
			MyReader.Close();
			SqlCommand CheckLastID = MyConnection.CreateCommand();
			CheckLastID.CommandText = "SELECT @@IDENTITY";
			NewSetupID = CheckLastID.ExecuteScalar().ToString();

			SqlCommand InsertRelation = MyConnection.CreateCommand();
			InsertRelation.CommandText = String.Format(@"
INSERT INTO relation_setups_to_users (username, setupid)
VALUES ('{0}', {1})", UserName, NewSetupID);

			MyReader = InsertRelation.ExecuteReader();
			MyReader.Close();
			MyConnection.Close();
			#endregion

			return NewSetupID;
		}
	}

	/// <summary>
	/// Summary description for NMRGenUtils
	/// </summary>
	public static class NMRGenUtils
	{
		/// <summary>
		/// Generates a unique ID
		/// </summary>
		/// <returns></returns>
		public static string GenerateId()
		{
			long i = 1;
			foreach (byte b in Guid.NewGuid().ToByteArray())
			{
				i *= ((int)b + 1);
			}
			return string.Format("{0:x}", i - DateTime.Now.Ticks);
		}
		
		/// <summary>
		/// Function that will capitalize first charachters in each word in a string.
		/// </summary>
		/// <param name="TextString">Text string where first letters in all words will get capitalized. </param>
		/// <returns></returns>
		public static string capitalizeAll(string TextString)
		{
			return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(TextString);
		}
		
		/// <summary>
		/// Check whether username exists in 'NMRUserData users' table and creates it, if not found.
		/// </summary>
		/// <param name="UserName">String, Username to check</param>
		/// <returns>string 'username'</returns>
		public static string checkUserCredentials(string UserName)
		{
			string resultid;
			SqlConnection MyConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NMRUserDataConnectionString"].ConnectionString);
			MyConnection.Open();

			SqlCommand UserCheckSELECTQuery = MyConnection.CreateCommand();
			UserCheckSELECTQuery.CommandText = "SELECT username FROM users AS u WHERE u.username='##currentusername##'".Replace("##currentusername##", SessionFacade.Username);
			Debug.WriteLine("Check Command " + UserCheckSELECTQuery.CommandText);

			SqlDataReader myReader = UserCheckSELECTQuery.ExecuteReader();
			if (!myReader.HasRows)
			{
				myReader.Close();
				UserCheckSELECTQuery.CommandText = "INSERT INTO users (username) VALUES ('##username##')".Replace("##username##", UserName);
				myReader = UserCheckSELECTQuery.ExecuteReader();
				Log.LogEntry("New user " + UserName + " is added to Spectrometers database.", EntryType.General, SessionFacade.Username);
				myReader.Close();
				UserCheckSELECTQuery.CommandText = "SELECT @@IDENTITY";
				resultid = UserCheckSELECTQuery.ExecuteReader().ToString();
				myReader.Close();
			}
			else
			{
				resultid = UserName;
			}

			MyConnection.Close();
			return resultid;
		}

		/// <summary>
		/// The function, that returns array of all the controls nested within <code>root</code> control.
		/// </summary>
		/// <param name="root">The root control all nested controls of which will be flattened into an array.</param>
		/// <returns>Array of Controls.</returns>
		public static Control[] FlattenHierachy(Control root)
		{
			List<Control> list = new List<Control>();
			list.Add(root);
			if (root.HasControls())
			{
				foreach (Control control in root.Controls)
				{
					list.AddRange(FlattenHierachy(control));
				}
			}
			return list.ToArray();
		}
		
		public static void createWikiPage(string Category, string ID, string Name)
		{
			string NewPageName;
			string NewPageTitle;
			
			List<string> Categories = new List<string>();
			Categories.Add(Category);
			NewPageTitle = Category + " " + Name;
			switch (Category)
			{
				case "Experiment":
					NewPageName = "Experiment_id_" + ID;
					break;
				case "Wave Shape":
					NewPageName = "WaveShape_id_" + ID;
					break;
				case "Pulse Program":
					NewPageName = "PulseProgram_id_" + ID;
					break;
				case "CPD Scheeme":
					NewPageName = "CPD_id_" + ID;
					break;
				case "Script":
					NewPageName = "Script_id_" + ID;
					break;
				default:
					throw new NotImplementedException("Some Unknown category was given to createWikiPage.");
			}

			addWikiPage(NewPageName, NewPageTitle, Categories);
		}

		private static bool addWikiPage(string NewPageName, string NewPageTitle, List<string> categories_names)
		{
			List<CategoryInfo> categories = new List<CategoryInfo>();

			PageInfo pg = Pages.Instance.FindPage(NewPageName);
			//Pages.Instance.CreateCategory("Experiment", (pg != null ? pg.Provider : null));
			bool bak = true;
			if (pg == null)
			{
				Pages.Instance.CreatePage(NewPageName);
				pg = Pages.Instance.FindPage(NewPageName);
				bak = false;
				//txtUploadDirectory.Text = pg.Name;
			}
			else
			{
				Log.LogEntry("Page " + NewPageName + " already exists: new version created.", EntryType.Error, SessionFacade.Username);
				return false;
			}
			Log.LogEntry("Page update requested for " + NewPageName, EntryType.General, SessionFacade.Username);
			Pages.Instance.ModifyPage(pg, NewPageTitle, SessionFacade.Username, DateTime.Now, "", "", bak);

			foreach (string name in categories_names)
			{
				Pages.Instance.CreateCategory(name);
				categories.Add(Pages.Instance.FindCategory(name));
			}
			Pages.Instance.Rebind(pg, categories.ToArray());

			return true;
		}

	}
}